using System;
using System.Runtime.Serialization;

namespace CommonTypes
{
	/// <summary>
	/// Structure for storing a point in 3d space.
	/// Declares an array accessor, as well as individual X, Y, Z Accessors.
	/// </summary>
	[Serializable]
	public class Vector: ISerializable
	{
		// Array to hold the coordinates of the point
		double[] m_Xyz = new double[3];

		public double[] XYZ
		{
			get { return m_Xyz; }
		}

		/// <summary>
		/// Constructor
		/// </summary>
		public Vector(double ix, double iy, double iz)
		{
			m_Xyz[0] = ix;
			m_Xyz[1] = iy;
			m_Xyz[2] = iz;
		}

		public Vector()
		{
			m_Xyz[0] = 0;
			m_Xyz[1] = 0;
			m_Xyz[2] = 0;
		}

		public override int GetHashCode()
		{
			return base.GetHashCode ();
		}


		/// <summary>
		/// Indexer for Vector
		/// </summary>
		public double this [int index]   // Indexer declaration
		{
			get 
			{
				// Check the index limits.
				if (index < 0 || index >= 3)
					return 0;
				else
					return m_Xyz[index];
			}
			set 
			{
				if (!(index < 0 || index >= 3))
					m_Xyz[index] = value;
			}
		}
		
		/// <summary>
		/// X property
		/// </summary>
		public double X 
		{ 
			get{ return m_Xyz[0]; } 
			set{ m_Xyz[0] = value;}
		}

		/// <summary>
		/// Y property
		/// </summary>
		public double Y 
		{ 
			get{ return m_Xyz[1]; } 
			set{ m_Xyz[1] = value;}
		}

		/// <summary>
		/// Z property
		/// </summary>
		public double Z 
		{ 
			get{ return m_Xyz[2]; } 
			set{ m_Xyz[2] = value;}
		}

		public void Set(double iX, double iY, double iZ)
		{
			m_Xyz[0] = iX;
			m_Xyz[1] = iY;
			m_Xyz[2] = iZ;
		}

		/// <summary>
		/// Normalizes the Vector.
		/// </summary>
		public void Normalize()
		{
			//Calculates the normals magnitude:
            double magnitude = Length();

			// Divides the normal by it's magnitude:
			m_Xyz[0] /= magnitude;
			m_Xyz[1] /= magnitude;
			m_Xyz[2] /= magnitude;
		}

        public double Length()
        {
            double magnitude = (double)System.Math.Sqrt
    (
    (m_Xyz[0] * m_Xyz[0]) +
    (m_Xyz[1] * m_Xyz[1]) +
    (m_Xyz[2] * m_Xyz[2])
    );
            return magnitude;
        }

		/// <summary>
		/// Vector addition
		/// </summary>
		/// <param name="v1">Vector 1</param>
		/// <param name="v2">Vector 2</param>
		public static Vector operator +(Vector v1, Vector v2)
		{
			return new Vector
				(
				v1[0] + v2[0], 
				v1[1] + v2[1], 
				v1[2] + v2[2]
				);
		}

		/// <summary>
		/// Vector subtraction
		/// </summary>
		/// <param name="v1">Vector 1</param>
		/// <param name="v2">Vector 2</param>
		public static Vector operator -(Vector v1, Vector v2)
		{
			return new Vector
				(
				v1[0] - v2[0], 
				v1[1] - v2[1], 
				v1[2] - v2[2]
				);
		}

		/// <summary>
		/// Vector multiplication with double
		/// </summary>
		public static Vector operator *(Vector v1, double num)
		{
			return new Vector
				(
				v1[0] * num, 
				v1[1] * num, 
				v1[2] * num
				);
		}

		/// <summary>
		/// Vector division with double
		/// </summary>
		public static Vector operator /(Vector v1, double num)
		{
			return new Vector
				(
				v1[0] / num, 
				v1[1] / num, 
				v1[2] / num)
				;
		}

        public void  Rotate( double angle)
        {
            double cosinus = System.Math.Cos(angle * System.Math.PI / 180.0);
            double sinus = System.Math.Sin(angle * System.Math.PI / 180.0);

            double x = X;
            double y = Y;
     
            this.X  = x * cosinus - y * sinus;
            this.Y  = x * sinus + y * cosinus;
        }



		public override bool Equals( object o )
		{
			if (this == (Vector)o)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
        public override string ToString()
        {
            return "X: " + X +" Y " + Y + " Z " + Z ;
        }

        public static implicit operator Vector (double[] coord)
        {
            return new Vector(coord[0], coord[1], coord[2]);
        }
		#region ISerializable Members

		protected Vector(SerializationInfo info, StreamingContext context)
		{
			string version = info.GetString("VectorVersion");

			// Make sure file format is not newer than the application:
			if( String.Compare(version, "1.0") > 0)
			{
				throw new Exception("Invalid file version!");
			}

			if( String.Compare(version, "1.0") >= 0)
			{
				m_Xyz = (double[])info.GetValue("m_Xyz", System.Type.GetType("System.Double[]") );
			}
		}

		public void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			info.AddValue("VectorVersion", "1.0"); // The class version

			info.AddValue("m_Xyz", m_Xyz);
		}

		#endregion
	}

}
